package cn.melonlib.cores.commons.comp.utils.impl;

import cn.melonlib.cores.commons.comp.utils.EntityWrapperContainer;
import cn.melonlib.cores.commons.comp.utils.IEntityWrapper;
import cn.melonlib.cores.commons.model.entity.IEntity;
import cn.melonlib.cores.commons.model.entity.StringIdEntity;
import cn.hutool.core.bean.BeanUtil;
import cn.melonlib.cores.jpa.comp.Dao;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.persistence.Entity;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.*;

@Component
public class JsonEntityWrapper extends ApplicationObjectSupport implements IEntityWrapper, ApplicationRunner {

    private ObjectMapper jsonMapper=new ObjectMapper();

    @Resource
    private EntityWrapperContainer entityWrapperContainer;

    @Resource
    private Dao dao;

    @Override
    public <ENTITY extends IEntity<? extends Serializable>> ENTITY wrap(String datas, Class<ENTITY> entityClass) throws Exception {
        Entity entityAnno=AnnotationUtils.findAnnotation(entityClass, Entity.class);
        if(entityAnno!=null) {
            Set<Field> fields = new HashSet<>();
            Class tmpclass = entityClass;
            this.logger.debug("JsonDatas:" + datas);
            Map<String, Object> params = jsonMapper.readValue(datas, Map.class);
            while (IEntity.class.isAssignableFrom(tmpclass)) {
                if (StringIdEntity.class.getName().equals(entityClass.getName())) {
                    break;
                }
                fields.addAll(Arrays.asList(tmpclass.getDeclaredFields()));
                tmpclass = tmpclass.getSuperclass();
            }
            IEntity entity = null;
            Serializable id = (Serializable) params.get("id");
            if (id != null && !"".equals(id)) {
                entity = this.dao.getEntity(entityClass, id);
            } else {
                entity = (IEntity) entityClass.newInstance();
            }

            for (Field field : fields) {
                Type decls = field.getGenericType();
                if ((decls instanceof Class) && IEntity.class.isAssignableFrom((Class<?>) decls)) {
                    try {
                        Serializable val = (Serializable) params.get(field.getName() + "_id");
                        if (val == null) {
                            continue;
                        }
                        if (val instanceof Number) {
                            val = ((Number) val).longValue();
                        }
                        if (val != null && !"".equals(val)) {
                            BeanUtil.setProperty(entity, field.getName(), this.dao.getEntity((Class) decls, val));
                        } else {
                            BeanUtil.setProperty(entity, field.getName(), null);
                        }
                    } catch (Exception e) {

                    }
                } else if (decls instanceof Class) {
                    Class<?> fcls = (Class<?>) decls;
                    Object val = params.get(field.getName());
                    if (val == null) {
                        continue;
                    }
                    if (val == null) {
                        BeanUtil.setProperty(entity, field.getName(), null);
                        continue;
                    }
                    if (CharSequence.class.isAssignableFrom(fcls)) {
                        System.out.println(field);
                        BeanUtil.setProperty(entity, field.getName(), val);
                    } else if (fcls.isEnum()) {
                        if (val instanceof Number) {
                            BeanUtil.setProperty(entity, field.getName(), Array.get(fcls.getMethod("values").invoke(null), ((Number) val).intValue()));
                        } else {
                            BeanUtil.setProperty(entity, field.getName(), fcls.getMethod("valueOf", String.class).invoke(null, val));
                        }
                    } else {
                        if (Double.class.isAssignableFrom((Class<?>) decls)) {
                            BeanUtil.setProperty(entity, field.getName(), ((Number) val).doubleValue());
                        } else if (Long.class.isAssignableFrom((Class<?>) decls)) {
                            BeanUtil.setProperty(entity, field.getName(), val);
                        } else if (Integer.class.isAssignableFrom((Class<?>) decls)) {
                            BeanUtil.setProperty(entity, field.getName(), NumberUtils.createInteger(val.toString()));
                        } else if (Date.class.isAssignableFrom((Class<?>) decls)) {
                            if (val instanceof Number) {
                                BeanUtil.setProperty(entity, field.getName(), new Date(((Number) val).longValue()));
                            } else {
                                BeanUtil.setProperty(entity, field.getName(), val);
                            }

                        } else if (((Class) decls).isEnum()) {
                            BeanUtil.setProperty(entity, fcls.getName(), fcls.getMethod("valueOf", String.class).invoke(null, val));
                        } else if (Boolean.class.isAssignableFrom((Class<?>) decls)) {
                            BeanUtil.setProperty(entity, field.getName(), BooleanUtils.toBoolean(val.toString()));
                        } else {
                            BeanUtil.setProperty(entity, field.getName(), val);
                        }
                    }
                }
            }
            return (ENTITY) entity;
        }else{
            return jsonMapper.readValue(datas,entityClass);
        }
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        this.entityWrapperContainer.addWrap(EntityWrapperContainer.JSON,this);
    }
}
